package com.efast.cafe.portal.security.core;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.session.FindByIndexNameSessionRepository;

import com.efast.cafe.framework.constant.CommonConstant;
import com.efast.cafe.framework.constant.SecurityConstant;
import com.efast.cafe.framework.enums.ResultStatus;
import com.efast.cafe.framework.redis.ObjectRedisTemplate;
import com.efast.cafe.framework.redis.locker.LockerFactory;
import com.efast.cafe.framework.redis.locker.RedissonDistributedLocker;
import com.efast.cafe.framework.spring.SpringContextUtils;
import com.efast.cafe.portal.bean.common.PortalUserDetails;
import com.efast.cafe.portal.bean.common.PortalUserSecurityBean;
import com.efast.cafe.portal.security.core.impl.PwdAuthenticationProcessor;
import com.efast.cafe.util.ConfigUtil;
import com.efast.cafe.util.StringUtil;

import net.sf.json.JSONObject;

public class AuthenticationFactory {
	
	//登录方式参数
	public static final String SPRING_SECURITY_LOGIN_TYPE="loginType";
	
	public static final String SPRING_SECURITY_FORM_COMPANYCODE_KEY = "companyCode";
	public static final String SPRING_SECURITY_FORM_UNAME_KEY = "username";
	public static final String SPRING_SECURITY_FORM_PWD_KEY = "password";
	
	//是否允许多地登录
	static boolean manysession = Boolean.parseBoolean(ConfigUtil.getValue("security.manysession"));
	
	public static boolean isManysession(){
		return manysession;
	}
	
	
	static final String DEFAULT_SPRING_SESSION_REDIS_PREFIX = "spring:session:";
	static final String PRINCIPAL_NAME_INDEX_NAME = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;

	/**
	 * 
	    * @Title: authenticationToken
	    * @Description: 登录拦截器
	    * @param @param request
	    * @param @param response
	    * @param @return    参数
	    * @return UsernamePasswordAuthenticationToken    返回类型
	    * @throws
	 */
	public static UsernamePasswordAuthenticationToken authenticationToken(HttpServletRequest request,HttpServletResponse response){
		String loginType = request.getParameter(SPRING_SECURITY_LOGIN_TYPE);
		
		AuthenticationTypeService service = getAuthenticationService(loginType);
		UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = service.authentication(request);//校验参数
		if(!manysession){
			//相同账号登录限制并发。
			String lockKey = "locker:appLogin:"+usernamePasswordAuthenticationToken.getName();
			RedissonDistributedLocker locker = LockerFactory.getLocker("Portal");
			boolean locked = false;
			try{
				//相同账号登录 并发限制 最多等待10秒，获取到锁最多保留60秒 60秒后自动释放锁
				locked = locker.tryLock(lockKey, 10, 60, TimeUnit.SECONDS);
				if(locked){
					return usernamePasswordAuthenticationToken;
				}else{
					JSONObject jo = new JSONObject();
					jo.put("success", false);
					jo.put("errorCode", ResultStatus.SYS_BUSY.getCode());
					jo.put("message", "当前用户并发登录过多 ，请稍后再试。");
					response.setCharacterEncoding("UTF-8"); 
					 response.setContentType("application/json; charset=utf-8");
					 try {
						response.getWriter().print(jo.toString());
					} catch (IOException e) {
						e.printStackTrace();
					}
					return null;
				}
				
			}finally{
				if(locker!=null && locked)
					locker.unlock(lockKey);
			}
		}else{
			return usernamePasswordAuthenticationToken;
		}
		
	}
	
	
	/**
	 * 
	    * @Title: onAuthenticationSuccess
	    * @Description: 登录成功
	    * @param @param request
	    * @param @param response
	    * @param @param authentication    参数
	    * @return void    返回类型
	    * @throws
	 */
	public static void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication)throws IOException, ServletException{
		String loginType = request.getParameter(SPRING_SECURITY_LOGIN_TYPE);
		if(!isManysession()){
			PortalUserDetails userDetail = (PortalUserDetails)authentication.getPrincipal();
			PortalUserSecurityBean user = userDetail.getUser();
			ObjectRedisTemplate objectRedisTemplate = SpringContextUtils.getBean(ObjectRedisTemplate.class);
			String key = new StringBuilder(DEFAULT_SPRING_SESSION_REDIS_PREFIX)
					.append("index:").append(PRINCIPAL_NAME_INDEX_NAME)
					.append(":").append(user.getUsername()).toString();
			Set<Object> sessionIds = objectRedisTemplate.boundSetOps(key)
					.members();
			//设置已登录用户的session 为失效。
			sessionIds.forEach(sid->{
				String otherSid = (String)sid;
					if(!request.getSession().getId().equals(otherSid)){
						objectRedisTemplate.boundSetOps(key).remove(otherSid);
						String skey = DEFAULT_SPRING_SESSION_REDIS_PREFIX+"sessions:"+otherSid;
						objectRedisTemplate.boundHashOps(skey).
							put("sessionAttr:"+CommonConstant.CURRENT_LOGIN_USER_FLAG, false);
						objectRedisTemplate.boundHashOps(skey).expire(30, TimeUnit.MINUTES);
					}
					
			});
		}
		getAuthenticationService(loginType).onAuthenticationSuccess(request, response, authentication);
	}
	
	/**
	 * 
	    * @Title: onAuthenticationFailure
	    * @Description: 登录失败
	    * @param @param request
	    * @param @param response
	    * @param @param exception    参数
	    * @return void    返回类型
	    * @throws
	 */
	public static void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException exception)throws IOException, ServletException{
		String loginType = request.getParameter(SPRING_SECURITY_LOGIN_TYPE);
		getAuthenticationService(loginType).onAuthenticationFailure(request, response, exception);
	}
	
	/**
	 * 
	    * @Title: onLogoutSuccess
	    * @Description: 登出成功
	    * @param @param request
	    * @param @param response
	    * @param @param authentication
	    * @param @throws IOException
	    * @param @throws ServletException    参数
	    * @return void    返回类型
	    * @throws
	 */
	public static void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, 
			Authentication authentication)throws IOException, ServletException{
		String loginType = request.getParameter(SPRING_SECURITY_LOGIN_TYPE);
		getAuthenticationService(loginType).onLogoutSuccess(request, response, authentication);
	}
	
	
	
	private static AuthenticationTypeService getAuthenticationService(String loginType){
		if(StringUtil.isNullOrBlank(loginType) || SecurityConstant.LoginType.LoginType_Pwd.equals(loginType)){
			return SpringContextUtils.getBean(PwdAuthenticationProcessor.class);
		}else {
			throw new AuthenticationServiceException(SecurityConstant.ERROR_CODE.LOGIN_TYPE_ERROR);
		}
	}
}
